/**
 * 获取access_token
 * 微信调用接口的唯一凭据
 * 特点：
 *    1.唯一的
 *    2.有效期2小时，提前5分钟请求
 * https请求方式: GET
 * https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
 * 设计思路：
 * 1.首次本地没有，发送请求获取到accesstoken，保存下来（本地文件）
 * 2.第二次或以后，先去读取本地文件，判断它是否过期，如果没有过期直接使用，如果过期了，重新请求并保存覆盖到原来的token
 */

//只需要引入request-promise-native
const rp = require('request-promise-native')
//引入fs模块，用于文件操作
const {writeFile,readFile} = require('fs')
//自定义的config模块
const {appid,appsecret} = require('../config')
const { Promise } = require('jszip/lib/external')

class wechat{
  constructor() {
  }

  /**
   * 获取access_token
   */
  getAccessToken(){
    //模板字符串定义一个url，请求地址
    const url = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appid}&secret=${appsecret}`
    //发送请求
    /*
    request
    request-promise-native 返回值是一个promise对象
     */
    return new Promise((resolve, reject)=>{
      rp({
        method: 'GET',
        url,//对象的简写方式，属性名和属性值名称一样
        json: true
      }).then(res=>{
        console.log(res)
        //设置accestoken的过期时间，
        res.expires_in = Date.now() + (res.expires_in-300) * 1000
        //将promise对象的状态改为成功
        resolve(res)
      })
        .catch(err=>{
          console.log(err)
          reject('getAccessToken方法出现问题：'+err)
        })
    })
  }

  /**
   * 保存access_token
   * @param accessToken accessToken
   */
  saveAccessToken(accessToken){
    //将对象转换成JSON字符串
    let str = JSON.stringify(accessToken)
    return new Promise((resolve, reject)=>{
      //将accessToken保存到文件中
      writeFile('./accessToken.txt',str,err=>{
        if(!err){
          console.log('文件保存成功')
          resolve()
        }else{
          reject('saveAccessToken方法出现问题：'+err)
        }
      })
    })
  }

  /**
   * 读取access_token
   */
  readAccessToken(){
    //读取本地文件中的token
    return new Promise((resolve, reject)=>{
      //将accessToken保存到文件中
      readFile('./accessToken.txt',(err,data) =>{
        if(!err){
          console.log('文件读取成功')
          resolve(JSON.parse(data))
        }else{
          reject('readAccessToken方法出现问题：'+err)
        }
      })
    })
  }

  /**
   * 校验accessToken是否过期
   * @param data data
   */
  isValidAccessToken (data) {
    /*
      判断凭据是否过期
        true   凭据没有过期
        false  凭据过期了
     */
    //过滤非法的数据
    if (!data || !data.access_token || !data.expires_in) return false;
    //判断凭据是否过期
    /*if (data.expires_in > Date.now()) {
      //如果凭据的过期时间大于当前时间，说明没有过期
      return true
    } else {
      //如果凭据的过期时间小于当前时间，说明过期了
      return false
    }*/
    //简写方式
    return data.expires_in > Date.now();
  }

  fetchAccessToken () {
    //优化操作,优化不去执行读取文件操作
    if (this.access_token && this.expires_in && this.isValidAccessToken(this)) {
      //说明this有凭据和过期时间，并且凭据未过期
      return Promise.resolve({access_token: this.access_token, expires_in: this.expires_in});
    }

    return this.readAccessToken()
      .then(async res => {
        //判断凭据是否过期(isValidAccessToken)
        if (this.isValidAccessToken(res)) {
          //没有过期，直接使用
          return Promise.resolve(res);
        } else {
          //重新发送请求获取凭据
          const data = await this.getAccessToken();
          //保存下来
          await this.saveAccessToken(data);
          //将请求回来的凭据返回出去
          return Promise.resolve(data);
        }
      })
      .catch(async err => {
        console.log(err);
        //重新发送请求获取凭据
        const data = await this.getAccessToken();
        //保存下来
        await this.saveAccessToken(data);
        //将请求回来的凭据返回出去
        return Promise.resolve(data);
      })
      .then(res => {
        //将其请求回来的凭据和过期时间挂载到this上
        this.access_token = res.access_token;
        this.expires_in = res.expires_in;
        //指定fetchAccessToken方法返回值
        return Promise.resolve(res);
      })
  }
}


const w = new wechat()
console.log(w.fetchAccessToken())

